<template>
    <div>
        <div class="legend">
            <div v-for="item in legend" style="display: flex;">
                <button @click="toggle(item.category)">{{item.type}}</button>
                <div :style="{'width': '50px','height': '30px', 'backgroundColor': item.color}"></div>
            </div>

        </div>
        <div ref="graph" id="graph"></div>
    </div>
</template>

<script>
import ForceGraph3D from "3d-force-graph";
import SpriteText from 'three-spritetext';
export default {
    name: 'view3d',
    props: {
        graphData: {
            type: Object,
            default() {
                return {}
            }
        },
        colors: {
            type: Array,
            default() {
                return []
            }
        },
        types: {
            type: Array,
            default() {
                return []
            }
        }
    },
    data() {
        return {
            myGraph: null,
            myGraphData: null,
            legendData: null,
            obj: null,
            decrementFlag: null,
            incrementFlag: null,
            legend: [],
            legendState: [
                { category: 0, state: true },
                { category: 1, state: true },
                { category: 2, state: true },
                { category: 3, state: true },
                { category: 4, state: true },
                { category: 5, state: true },
                { category: 6, state: true }
            ],
            all_relation: []
        }
    },
    methods: {
        deepClone(obj) {
            return JSON.parse(JSON.stringify(obj))
        },
        init(graphData) {
            // console.log(graphData);
            graphData.links.forEach(link => {
                const a = graphData.nodes[link.source];
                const b = graphData.nodes[link.target];
                !a.neighbors && (a.neighbors = []);
                !b.neighbors && (b.neighbors = []);
                a.neighbors.push(b);
                b.neighbors.push(a);

                !a.links && (a.links = []);
                !b.links && (b.links = []);
                a.links.push(link);
                b.links.push(link);
            });

            const highlightNodes = new Set();
            const highlightLinks = new Set();
            let hoverNode = null;
            this.myGraph = ForceGraph3D(
                {
                    controlType: "trackball",                                                       // orbit沿2d轨迹绕着拖动，fly 固定不动
                    rendererConfig: { antialias: true, alpha: true }
                }
            )

            this.myGraph(this.$refs.graph)
                .graphData(graphData)
                // .width(600)
                // .height(600)
                .showNavInfo(false)
                .backgroundColor('#fff')
                .nodeThreeObject(node => {
                    return this.addSpriteText(node);
                })
                .nodeResolution(12)
                .nodeThreeObjectExtend(true)
                .nodeRelSize(7)
                // .nodeOpacity(0.8)
                // .nodeAutoColorBy('category')
                .linkVisibility(true)
                // // .linkColor(()=>'black') 
                .linkDirectionalArrowRelPos(1)
                .linkLabel(r => r.value)
                .linkDirectionalArrowLength(3)
                // .linkDirectionalArrowRelPos(1)
                // .linkCurvature(0.25)
                // .linkAutoColorBy(r => r.category)   
                .linkThreeObjectExtend(true)
                .linkThreeObject(link => {
                    // extend link with text sprite
                    const sprite = new SpriteText(link.value);
                    sprite.color = 'black';
                    sprite.textHeight = 3;
                    // sprite.position.set(0,12,0);
                    return sprite;
                })
                .linkPositionUpdate((sprite, { start, end }) => {
                    const middlePos = Object.assign(
                        ...['x', 'y', 'z'].map(c => ({
                            [c]: start[c] + (end[c] - start[c]) / 3 // calc middle point
                        }))
                    );
                    // Position sprite
                    Object.assign(sprite.position, middlePos);
                })

                .onNodeClick(node => {                                                            // 点击节点事件（视角转移到该节点）
                    // Aim at node from outside it
                    const distance = 70;
                    const distRatio = 1 + distance / Math.hypot(node.x, node.y, node.z);
                    this.myGraph.cameraPosition(
                        { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }, // new position
                        node, // lookAt ({ x, y, z })
                        3000  // ms transition duration)
                    )
                })
                // start
                .nodeColor(node => {
                    if (highlightNodes.has(node)) {
                        if (node === hoverNode) return 'rgb(255,0,0,1)'
                        else return 'rgba(255,160,0,0.5)'
                    }
                    else {
                        return this.colors[node.category]
                    }

                })
                .linkWidth(link => (highlightLinks.has(link) ? 4 : 1))
                .linkDirectionalParticles(link => (highlightLinks.has(link) ? 4 : 0))
                .linkDirectionalParticleWidth(4)
                .onNodeHover(node => {
                    // no state change
                    if ((!node && !highlightNodes.size) || (node && hoverNode === node)) return;

                    highlightNodes.clear();
                    highlightLinks.clear();
                    if (node) {
                        highlightNodes.add(node);
                        node.neighbors.forEach(neighbor => highlightNodes.add(neighbor));
                        node.links.forEach(link => highlightLinks.add(link));
                    }

                    hoverNode = node || null;

                    this.updateHighlight();
                })
                .onLinkHover(link => {
                    highlightNodes.clear();
                    highlightLinks.clear();
                    if (link) {
                        highlightLinks.add(link);
                        highlightNodes.add(link.source);
                        highlightNodes.add(link.target);
                    }
                    this.updateHighlight();
                })
            console.log('初始化完成');
        },
        //添加文字
        addSpriteText(node) {
            const sprite = new SpriteText(node.name);
            sprite.color = '#FF0000';
            sprite.textHeight = 7;
            sprite.position.set(0, 12, 0);
            return sprite;
        },
        updateHighlight() {
            // trigger update of highlighted objects in scene
            this.myGraph
                .nodeColor(this.myGraph.nodeColor())
                .linkWidth(this.myGraph.linkWidth())
                .linkDirectionalParticles(this.myGraph.linkDirectionalParticles())
        },
        parserByCategory(category) {
            // console.log(o);
            // console.log(this.myGraphData.links[0].target);

            const ids = []
            const nodes = this.myGraphData.nodes.filter(item => {
                if (item.category === category) ids.push(item.id)
                return item.category === category
            })
            // console.log(nodes);
            // console.log(ids);
            const links = this.myGraphData.links.filter(item => {
                // console.log(item.source.id);
                return ids.includes(item.source) || ids.includes(item.target)
                // return ids.indexOf(item.source.id) !== -1 || ids.indexOf(item.target.id) !== -1
            })
            // console.log(links);
            let obj = {
                nodes,
                links
            }
            return obj
        },
        getLegend() {
            let size = this.types.length;
            for (let i = 0; i < size; i++) {
                let a = {};
                a.type = this.types[i];
                a.color = this.colors[i];
                a.category = i
                this.legend.push(a);
            }
            // console.log(this.legend);
        },
        toggle(category) {
            this.legendState.forEach(item => {
                if (item.category === category) {
                    item.state = !item.state
                }
            })
            // 根据legendState数组中state的值来决定渲染
        },
        // 删除方法,category为删除的家族id。删除相关的node和links
        delete(category, obj) {
            let ids = []
            // console.log(obj.links[0].source);
            const nodes = obj.nodes.filter(item => {
                if (item.category === category) ids.push(item.id)
                return item.category !== category
            })
            const links = obj.links.filter(item => {
                return !ids.includes(item.source.id) && !ids.includes(item.target.id)
            })
            const o = {
                nodes,
                links
            }
            return o
        },
        // 添加方法
        add(category) {
            const ids = []
            const currentIds = []
            this.myGraphData.nodes.forEach(item => {
                currentIds.push(item.id)
            })
            // 1. 添加自己家族的节点和关系
            console.log(11);
            this.all_relation[category].nodes.forEach(item => {
                if (item.category === category) {
                    this.myGraphData.nodes.push(item)
                    // 获得家族成员的id
                    ids.push(item.id)
                }
            })
            this.all_relation[category].links.forEach((item) => {
                if (ids.includes(item.source.id) && ids.includes(item.target.id)) {
                    this.myGraphData.links.push(item)
                }
            })
            console.log(ids);
            console.log(this.myGraphData);

            // 2. 判断场上的节点和自己家族有没有节点关系，有：添加
            this.all_relation[category].links.forEach(item => {
                if ((ids.includes(item.source.id) && currentIds.includes(item.target.id)) || (currentIds.includes(item.source.id) && ids.includes(item.target.id))) {
                    this.myGraphData.links.push(item)
                }
            })
        }
    },
    mounted() {
        // console.log(this.parserByCategory(0, this.graphData));
        this.getLegend()
        // 对props对象进行深拷贝
        this.myGraphData = this.deepClone(this.graphData)

        // console.log(this.graphData);
        // console.log(this.myGraphData);
        this.init(this.myGraphData)
        // console.log(this.myGraphData);
        // 获取所有的家族信息
        const obj0 = this.parserByCategory(0)
        const obj1 = this.parserByCategory(1)
        const obj2 = this.parserByCategory(2)
        const obj3 = this.parserByCategory(3)
        const obj4 = this.parserByCategory(4)
        const obj5 = this.parserByCategory(5)
        const obj6 = this.parserByCategory(6)
        this.all_relation.push(obj0)
        this.all_relation.push(obj1)
        this.all_relation.push(obj2)
        this.all_relation.push(obj3)
        this.all_relation.push(obj4)
        this.all_relation.push(obj5)
        this.all_relation.push(obj6)
        // console.log(obj0);
    },
    computed: {
        newLegend() {
            return JSON.parse(JSON.stringify(this.legendState))
        }
    },
    watch: {
        newLegend: {
            deep: true,
            handler: function (newValue, oldValue) {
                // 跟旧数组进行比较，得出需要进行的是增加操作还是删除操作，找到category
                for (let i = 0; i < newValue.length; i++) {

                    if (newValue[i].state !== oldValue[i].state) {
                        if (!newValue[i].state) {
                            // 删除
                            this.decrementFlag = i
                        } else {
                            this.incrementFlag = i
                        }
                    }
                }
                // console.log(this.decrementFlag);
                // console.log(this.incrementFlag);
                if (this.decrementFlag !== null) {
                    this.myGraphData = this.delete(this.decrementFlag, this.myGraphData)
                    // console.log(this.obj);
                    // console.log(this.myGraphData);
                    this.myGraph.graphData(this.myGraphData)
                    // return
                    // this.myGraph.refresh()
                    // console.log(this.myGraphData);
                }
                // 1. 数据更新,需要传category
                // 2. 数据已经更新，refresh()
                if (this.incrementFlag !== null) {
                    console.log(this.myGraphData);
                    // 添加操作
                    const currentIds = []
                    this.myGraphData.nodes.forEach(item => {
                        currentIds.push(item.id)
                    })
                    // 当前的节点id数组

                    console.log(currentIds);
                    this.add(this.incrementFlag)
                    // this.myGraphData.links = this.myGraphData.links.filter(item => {
                    //     console.log(item.source.id);
                    //     // return currentIds.includes(item.source.id) && currentIds.includes(item.target)
                    // })
                    // console.log(this.myGraphData);

                    this.myGraph.graphData(this.myGraphData)
                    // this.myGraph.refresh()
                }
                this.decrementFlag = null
                this.incrementFlag = null
                // console.log(this.myGraphData);
            }
        }
    }
}
</script>

<style scoped>
.legend {
    display: flex;
}
</style>

